package core;
//import java.awt.image.BufferedImage;
import java.util.PriorityQueue;
import java.lang.Math.*;

public class SeamMap {
    
    private enum Direction {
        LEFT, RIGHT, UP, DOWN
    }
    /* Overlays directly with the EnergyMap */
    private PixelNode[][] pixelMap;
    
    /* the head of all the seam L.L.'s */
    PixelNode[] seamHeads;
    /* The EnergyMap used to derive the SeamMap*/
    private EnergyMap energyMap;
    
    private int width;
    private int height;
    
    /* SeamMap constuctor.
     * Takes an EnergyMap and returns a completed SeamMap 
     */
    public SeamMap(EnergyMap eMap) {
       energyMap = eMap;
       width = energyMap.getWidth();
       height = energyMap.getHeight();
       
       pixelMap = new PixelNode[width][height];
       initPixelMap(pixelMap, eMap);
       mapSeams();  
    }

    /* Maps the initial EnergyMap to the SeamMap's pixelMap
     */
    private void initPixelMap(PixelNode[][] pixelMap, EnergyMap energyMap) {
        int x, y;
        for(x=0;x<width;x++) {
            for(y=0;y<height;y++) {
                pixelMap[x][y] = new PixelNode(x, y, energyMap.getEnergy(x, y));
            }
        }
    }



    
    private void mapSeams( ) {
        int i=0, x = 0;
        double avg = averageTopVariation();
        seamHeads = new PixelNode[width/2];
        do {
            x = seamStart(x, avg);
            mapSeam(x);
            seamHeads[i++] = pixelMap[x][height-1];
        } while (x < width-1 && x > 0 && i<width/2);
        
    }
    
    
    private int seamStart(int x, double avg) {
        double diff;
        
        for(; x < width; x++) {
            diff = Math.abs( pixelMap[x][0].getEnergy() 
                           - pixelMap[x+1][0].getEnergy() );
            if(diff > avg) { return x; 
            } else continue;
        }
        return 0;
    }
    
    
    /* Gets the average energy variation along the top of the image.
     * Takes the top 3 pixels for each y as a sample, averages them,
     * and measures the difference with its neighbors.
     */
    private double averageTopVariation() {
        int x, y, ct;    
        double diffAvg=0, avg[];
        
        avg = new double[width];
        
        for(x=0; x < width; x++) {
            y = height;
            avg[x] = (pixelMap[x][y].getEnergy() + 
            pixelMap[x][y-1].getEnergy() + pixelMap[x][y-2].getEnergy()) / 3;
        }
    
        for(x=0; x<width; x++) {
            diffAvg += Math.abs(avg[x] - avg[x-1]);      
        }
        return diffAvg / x;
    }
    
 /*   double getSeam(PixelNode origin, double leastCost, Direction from) {
        PixelNode p;
        double cost, left, right, up, down;
        int x, y;
        
        x = origin.getX();
        y = origin.getY();      
        if(y = 0) { return origin.getEnergy();

        cost = origin.getEnergy();
        if( from != UP ) {
            up = getSeam( pixelMap[x][y], DOWN);
        if(from != DOWN) { 
            down = getSeam( pixelMap[x][y-1], UP); 
        }
        if(from != LEFT && x > 0) { 
            left = getSeam( pixelMap[x][y], RIGHT);
        }
        if(from != RIGHT && x < width-1) { 
            right = getSeam( pixelMap[x][y], LEFT);
        }
        
                
    }*/

    private void mapSeam(int startX) {
        PixelNode p, origin;
        int x, y;
        double bias = .01;
        
        y = height-1;
        p = origin = pixelMap[startX][y];
        while(y > 0) {      // when we hit the bottom, we're finished
            p.next = getNextNode(p, bias);  
            while(p == p.next) { // we can do this because they're in the same array
                p.next = getNextNode(p, (bias*=2));//keep pushing it downward
            }
            p = p.next;
            y = p.getY(); // we need to be able to go back somewhat, so this
        }                 // doesn't necessarily progress constantly
    }

    private PixelNode getNextNode(PixelNode p, double bias) {
        int x, y;
        PixelNode current = null;
        double e;   

        x = p.getX();
        y = p.getY();
        /* up first, but bias against */
        if(y < height-1) {
            e = pixelMap[x][y+1].getEnergy();
            e += (e * (bias * 2));
            current = pixelMap[x][y+1];
        } else { e = 2;
        }
        if( x>0 && ((pixelMap[x-1][y].getEnergy() * bias) < e) ) {
            e = pixelMap[x-1][y].getEnergy() * bias;
            current = pixelMap[x-1][y];
        }
        if( x<(width-1) && (pixelMap[x+1][y].getEnergy() * bias) < e ) {
            e = pixelMap[x+1][y].getEnergy() * bias;
            current = pixelMap[x+1][y];
        }
        if(pixelMap[x][y-1].getEnergy() < e) { 
            current = pixelMap[x][y-1]; 
        }
        return current;
    }

        
            
}
